//#define USE_DUNGEONBUDDY_DLL
using System;
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using CommonBehaviors.Decorators;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.CommonBot.POI;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Wrath_of_the_Lich_King
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Wrath_of_the_Lich_King
#endif
{
    public class TheCullingOfStratholme : Dungeon
    {
        #region Overrides of Dungeon

        private const uint ArthasId = 26499;

        public override uint DungeonId
        {
            get { return 209; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(-8756.695, -4461.556, -200.9562); }
        }

        public override void OnEnter()
        {
            _cratesDone = _gauntletStarted = _arthusSummoned = _gauntlet2Done = false;
            _suspiciousCrates.CycleTo(_suspiciousCrates.First);
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret as WoWUnit;
                    if (unit != null)
                    {
                        if (unit.Entry == RisenZombieId && !unit.Combat )
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.CurrentTargetGuid != 0 && unit.CurrentTarget.Entry == ArthasId && StyxWoW.Me.IsLeader())
                    {
                        outgoingunits.Add(unit);
                    }
                    if (unit.Entry == RisenZombieId && unit.Combat && StyxWoW.Me.IsDps()) // Risen Zombie.. kill these buggers
                        outgoingunits.Add(unit);
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (unit.CurrentTargetGuid != 0 && unit.CurrentTarget.Entry == ArthasId && StyxWoW.Me.IsLeader())
                        priority.Score += 150;
                }
            }
        }

        #endregion

        private const float PIx2 = (float)Math.PI * 2;
        private const uint RisenZombieId = 27737;
        // chomie at dungeon entrance
        private const uint ChromieAtEntranceId = 26527;
        private const uint ArcaneDisruptorId = 37888;
        private const uint ChromieOutsideTownId = 27915;
        private const uint ChronoLordEpochId = 26532;
        private readonly Landmarks _landmarks = new Landmarks();

        private readonly CircularQueue<WoWPoint> _suspiciousCrates = new CircularQueue<WoWPoint>
                                                                     {
                                                                         new WoWPoint(1579.717, 622.6676, 99.81494),
                                                                         new WoWPoint(1571.189, 669.42, 103.0486),
                                                                         new WoWPoint(1629.116, 730.6912, 113.3107),
                                                                         new WoWPoint(1629.287, 812.0453, 121.4282),
                                                                         new WoWPoint(1667.026, 871.1566, 119.8063),
                                                                     };


        private bool _arthusSummoned;
        private bool _cratesDone;
        private bool _gauntlet2Done;
        private bool _gauntletStarted;

        private LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        private WoWUnit Arthas
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == ArthasId); }
        }

        private WoWPoint GetNextPackLocationFromLandMark()
        {
            _landmarks.Refresh();
            var lm = _landmarks.LandmarkList.FirstOrDefault();
            var landmarkLoc = lm != null ? lm.Location : WoWPoint.Zero;

            if (landmarkLoc != WoWPoint.Zero && !Navigator.CanNavigateFully(StyxWoW.Me.Location, landmarkLoc))
            {
                for (int range = 5; range <= 20; range += 5)
                {
                    for (float step = 0f; step < 1f; step += 0.05f)
                    {
                        var newPoint = landmarkLoc.RayCast(PIx2 * step, range);
                        var heights = Navigator.FindHeights(newPoint.X, newPoint.Z);
                        if (!heights.Any()) continue;
                        newPoint.Z = heights.Max();
                        if (Navigator.CanNavigateFully(StyxWoW.Me.Location, newPoint))
                            return newPoint;
                    }
                }
            }
            return landmarkLoc;
        }

        [EncounterHandler(26527, "Chromie", Mode = CallBehaviorMode.Proximity, BossRange = 35)]
        public Composite QuestPickupHandler()
        {
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.Available,
                    ScriptHelpers.CreatePickupQuest(() => unit)),
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.TurnIn,
                    ScriptHelpers.CreateTurninQuest(() => unit)));
        }

        [EncounterHandler(26527, "Chromie", Mode = CallBehaviorMode.Proximity)]
        public Composite ChromieEncounter()
        {
            WoWUnit boss = null;


            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                // take teleport or pickup Arcane Disruptor .
                new Decorator(ctx => !ScriptHelpers.IsBossAlive("Chrono-Lord Epoch") || _gauntletStarted, ScriptHelpers.CreateTalkToNpc(() => boss)),
                new Decorator(
                    ctx => !_cratesDone && !StyxWoW.Me.BagItems.Any(b => b.IsValid && b.Entry == 37888) && boss.QuestGiverStatus == QuestGiverStatus.None,
                    new Sequence(
                        ScriptHelpers.CreateTalkToNpcContinue(() => boss),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(() => boss),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(() => boss),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(() => boss),
                        new WaitContinue(4, ctx => false, new ActionAlwaysSucceed()),
                        new DecoratorContinue(ctx => !StyxWoW.Me.BagItems.Any(b => b.IsValid && b.Entry == ArcaneDisruptorId), new Action(ctx => _cratesDone = true)))));
        }

        private Composite CratesEncounter()
        {
            var chromieLoc = new WoWPoint(1550.081, 574.4117, 92.60664);
            WoWGameObject crate = null;
            WoWItem disruptor = null;
            WoWPoint movetoLoc = WoWPoint.Zero;

            return new PrioritySelector(
                ctx =>
                {
                    disruptor = StyxWoW.Me.BagItems.FirstOrDefault(b => b.Entry == ArcaneDisruptorId);
                    movetoLoc = _suspiciousCrates.Peek();
                    return crate = ObjectManager.GetObjectsOfType<WoWGameObject>().Where(o => o.Entry == 190094).OrderBy(o => o.DistanceSqr).FirstOrDefault();
                },
                // talk to chromie to get the disruptor
                new Decorator(
                    ctx => !StyxWoW.Me.BagItems.Any(b => b.IsValid && b.Entry == ArcaneDisruptorId),
                    new PrioritySelector(new Decorator(ctx => StyxWoW.Me.Location.DistanceSqr(chromieLoc) > 10 * 10, new Action(ctx => Navigator.MoveTo(chromieLoc))))),
                new Decorator(
                    ctx => crate != null && crate.DistanceSqr <= 200 * 200,
                    new Sequence(
                        new ActionSetActivity("Using Arcane Disruptor on Suspicious Grain Crate"),
                        ScriptHelpers.CreateMoveToContinue(() => crate),
                        new Action(ctx => disruptor.UseContainerItem()))),
                new Decorator(
                    ctx => StyxWoW.Me.Location.DistanceSqr(movetoLoc) < 15 * 15,
                    new Sequence(
                        new Action(ctx => _suspiciousCrates.Dequeue()),
                        new DecoratorContinue(ctx => _suspiciousCrates.Peek() == _suspiciousCrates.First, new Action(ctx => _cratesDone = true)))),
                new Action(ctx => Navigator.MoveTo(movetoLoc)));
        }

        private Composite GauntletEncounter()
        {
            WoWUnit arthas = null, chromieAtOutsideTown = null;

            var questTurninLoc = new WoWPoint(1813.298, 1283.578, 142.2429);
            var chromieStartEventLoc = new WoWPoint(1813.298, 1283.578, 142.2428);
            var arthasStartEventLoc = new WoWPoint(2047.948, 1287.598, 142.8352);
            var packLoc = WoWPoint.Zero;

            return new PrioritySelector(
                ctx =>
                {
                    arthas = Arthas;
                    chromieAtOutsideTown = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == ChromieOutsideTownId);
                    packLoc = GetNextPackLocationFromLandMark();
                    if (!_arthusSummoned)
                    {
                        if (arthas != null || packLoc != WoWPoint.Zero)
                            _arthusSummoned = true;
                    }

                    if (packLoc != WoWPoint.Zero && !_gauntletStarted)
                        _gauntletStarted = true;
                    if (_gauntletStarted && !_cratesDone)
                        _cratesDone = true;
                    var gate = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(o => o.Entry == 188686);
                    if (gate != null && gate.State == WoWGameObjectState.Active)
                    {
                        if (ScriptHelpers.IsBossAlive("Chrono-Lord Epoch"))
                            ScriptHelpers.MarkBossAsDead("Chrono-Lord Epoch");
                        if (!_cratesDone)
                            _cratesDone = true;
                    }
                    return ctx;
                },
                new Decorator(ctx => !_cratesDone, CratesEncounter()),
                // move to pack location
                new Decorator(
                    ctx => packLoc != WoWPoint.Zero && StyxWoW.Me.IsTank() && Targeting.Instance.FirstUnit == null && StyxWoW.Me.Location.DistanceSqr(packLoc) > 10 * 10,
                    new Action(ctx => ScriptHelpers.MoveTankTo(packLoc))),
                new Decorator(
                    ctx => chromieAtOutsideTown != null && chromieAtOutsideTown.QuestGiverStatus == QuestGiverStatus.Available,
                    ScriptHelpers.CreatePickupQuest(() => chromieAtOutsideTown)),
                // turn in Dispelling Illusions and pickup the followup.
                new Decorator(
                    ctx => StyxWoW.Me.QuestLog.ContainsQuest(13149) && StyxWoW.Me.QuestLog.GetQuestById(13149).IsCompleted,
                    new Sequence(ScriptHelpers.CreateMoveToContinue(() => questTurninLoc), ScriptHelpers.CreateTurninQuest(() => chromieAtOutsideTown))),
                // summon arthas.
                new Decorator(
                    ctx => _cratesDone && !_arthusSummoned,
                    new Sequence(
                        ScriptHelpers.CreateMoveToContinue(() => chromieStartEventLoc),
                        ScriptHelpers.CreateTalkToNpcContinue(ChromieOutsideTownId),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(ChromieOutsideTownId),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(ChromieOutsideTownId),
                        new WaitContinue(2, ctx => false, new ActionAlwaysSucceed()),
                        ScriptHelpers.CreateTalkToNpcContinue(ChromieOutsideTownId),
                        new Action(ctx => _arthusSummoned = true))),
                new Decorator(
                    ctx => _arthusSummoned && !_gauntletStarted && StyxWoW.Me.Location.DistanceSqr(arthasStartEventLoc) > 25 * 25,
                    new Action(ctx => ScriptHelpers.MoveTankTo(arthasStartEventLoc))),
                // talk to arthus to start the event.
                new Decorator(
                    ctx => arthas != null && arthas.Location.DistanceSqr(arthasStartEventLoc) <= 3 * 3 && StyxWoW.Me.PartyMembers.Count(u => u.DistanceSqr <= 40 * 40) == 4,
                    new Sequence(ScriptHelpers.CreateTalkToNpc(() => arthas), new Action(ctx => _gauntletStarted = true))),
                // check if a hidden door is open meaning that we allready killed this boss and should move on.
                new Decorator(
                    ctx => _arthusSummoned && !_gauntletStarted && StyxWoW.Me.Location.DistanceSqr(arthasStartEventLoc) <= 25 * 25,
                    new PrioritySelector(
                        new ActionAlwaysSucceed())),
                // Wait for packs to spawn.
                new Decorator(
                    ctx =>
                    (packLoc == WoWPoint.Zero || StyxWoW.Me.Location.DistanceSqr(packLoc) <= 10 * 10) && _gauntletStarted && StyxWoW.Me.IsTank() &&
                    Targeting.Instance.FirstUnit == null,
                    new ActionAlwaysSucceed()));
        }


        [EncounterHandler(26529, "Meathook")]
        public Composite MeathookEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }


        [EncounterHandler(26530, "Salramm the Fleshcrafter")]
        public Composite SalrammTheFleshcrafterEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }

        [EncounterHandler(26532, "Chrono-Lord Epoch", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite ChronoLordEpochEncounter()
        {
            var arthasStartLoc = new WoWPoint(2366.24, 1195.253, 131.9611);
            var arthasEndLoc = new WoWPoint(2425.898, 1118.842, 148.0759);

            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(ctx => ScriptHelpers.IsBossAlive("Meathook") || ScriptHelpers.IsBossAlive("Salramm the Fleshcrafter"), GauntletEncounter()),
                new Decorator(
                    ctx => !ScriptHelpers.IsBossAlive("Meathook") && !ScriptHelpers.IsBossAlive("Salramm the Fleshcrafter"),
                    ScriptHelpers.CreateTankTalkToThenEscortNpc(ArthasId, arthasStartLoc, arthasEndLoc)));
        }

        [EncounterHandler(32273, "Infinite Corruptor")]
        public Composite InfiniteCorruptorEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }

        [EncounterHandler(26533, "Mal'Ganis", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite MalGanisGaunletEncounter()
        {
            var arthasStartLoc = new WoWPoint(2425.898, 1118.842, 148.0759);
            var arthasGauntletStartLoc = new WoWPoint(2534.988, 1126.163, 130.7619);
            var arthasGauntletEndLoc = new WoWPoint(2363.44, 1404.906, 128.7849);

            WoWUnit boss = null, arthas = null, chromie = null;
            WoWGameObject chest = null;
            WoWGameObject door = null;
            return new PrioritySelector(
                ctx =>
                {
                    chromie = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == 26527);
                    chest = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(o => o.Entry == 190663 && o.CanUse() && !o.InUse);
                    arthas = Arthas;
                    door = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(g => g.Entry == 187723);
                    if (door != null && door.State == WoWGameObjectState.Active && !_gauntlet2Done)
                        _gauntlet2Done = true;
                    if (!_gauntlet2Done && arthas != null)
                        _gauntlet2Done = Arthas.Location.DistanceSqr(arthasGauntletEndLoc) <= 3 * 3;
                    return boss = ctx as WoWUnit;
                },
                new Decorator(
                // talk to arthas so he'll open the secret door and run down to the gauntlet start
                    ctx => arthas != null && arthas.Location.DistanceSqr(arthasStartLoc) <= 3 * 3 && arthas.CanGossip, ScriptHelpers.CreateTalkToNpc(() => arthas)),
                new Decorator(
                    ctx => arthas != null && arthas.Location.DistanceSqr(arthasGauntletEndLoc) <= 3 * 3 && arthas.CanGossip, ScriptHelpers.CreateTalkToNpc(() => arthas)),
                ScriptHelpers.CreateTankTalkToThenEscortNpc(
                    ArthasId,
                    0,
                    arthasGauntletStartLoc,
                    arthasGauntletEndLoc,
                    3f,
                    () => _gauntlet2Done && (arthas == null || arthas.Location.DistanceSqr(arthasGauntletStartLoc) <= 3 * 3)));
        }

        [EncounterHandler(26533, "Mal'Ganis")]
        public Composite MalGanisEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => Me.IsFollower() && boss.CurrentTargetGuid != Me.Guid && !boss.IsMoving && boss.Distance < 15, () => boss, new ScriptHelpers.AngleSpan(0, 180)),
                // face boss away from group because of the Carrion Swarm
                new Decorator(ctx => boss != null && StyxWoW.Me.IsTank() && boss.CurrentTarget == StyxWoW.Me, ScriptHelpers.CreateTankFaceAwayGroupUnit(15)));
        }

        [ObjectHandler(190663, "Chest")]
        public Composite ChestBehavior()
        {
            WoWGameObject chest = null;
            return new PrioritySelector(
                ctx => chest = ctx as WoWGameObject,
                new Decorator(
                    ctx => !chest.InUse && chest.CanUse() && !Blacklist.Contains(chest, BlacklistFlags.Loot),
                    new PrioritySelector(
                        new Decorator(ctx => !chest.WithinInteractRange, new Action(ctx => Navigator.MoveTo(chest.Location))),
                        new Sequence(
                            new DecoratorContinue(ctx => Me.IsMoving, new Action(ctx => WoWMovement.MoveStop())),
                            new WaitContinue(2, ctx => !Me.IsMoving, new ActionAlwaysSucceed()),
                            new Action(ctx => chest.Interact()),
                            new WaitContinue(3, ctx => false, new ActionAlwaysSucceed()),
                            new Action(ctx => LootFrame.Instance.LootAll()),
                            new Action(ctx => Blacklist.Add(chest, BlacklistFlags.Loot, TimeSpan.FromMinutes(10)))))));
        }
    }
}